/* 拓扑排序
* 1.概念:
    首先遍历所有点，将入度为零的点加入队列，然后依次遍历出队的点的所有临边，并将临点的入度-1，若为0则加入队列。
    最终队列中的顺序就是拓扑排序的顺序

* 本题: 
    反向拓扑 
    边 u->v: u>v
    虚拟源点
    火车u在车站v停下,则边权相连为1, 若不停, 则边权相连为0
*/

#pragma GCC optimize("O1,O2,O3,Ofast")
#pragma GCC optimize("no-stack-protector,unroll-loops,fast-math,inline")
#pragma GCC target("avx,avx2,fma")
#pragma GCC target("sse,sse2,sse3,sse4,sse4.1,sse4.2,ssse3")

#include <iostream>
#include <cstring>
#include <algorithm> 
// #define ONLINE_GUDGE
using namespace std;
const int N = 2010, M = 1000010;

int n, m;
int h[N], e[M], w[M], ne[M], idx;
int q[N], d[N], dist[N];
bool st[N];

void AddEdge(int a, int b, int c)
{ e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++; d[b]++;} //

void TopSort()
{
    int hh = 0, tt = -1;
    for(int u = 1; u <= n + m; u++){
        if(!d[u]) q[++tt] = u; // 初始化
    }
        
    while(hh <= tt)
    {
        auto u = q[hh++];
        for(int i = h[u]; ~i; i = ne[i]){
            auto v = e[i];
            if(--d[v] == 0) q[++tt] = v;
        }
    }
}

int main()
{

    #ifdef ONLINE_JUDGE

    #else
    freopen("./in.txt","r",stdin);
    #endif
    ios::sync_with_stdio(false);   
	cin.tie(0);
    
    cin >> n >> m;
    memset(h, -1, sizeof h);
    

    for(int i = 1; i <= m; i++){
        memset(st, 0, sizeof st);
        int cnt; cin >> cnt; // 火车i有cnt个停靠站
        int start = n, end = 1;
        while(cnt--){ 
            int stop; cin >> stop; // 在火车站停下
            start = min(start, stop);
            end = max(end, stop);
            st[stop] = true;
        }
        // printf("stop[%d]: %d-%d\n", i, start, end);
        
        int v = n + i; // 虚拟节点 i -v-> stop
        for(int i = start; i <= end; i++){
            if(!st[i]) AddEdge(i, v, 0);
            else AddEdge(v, i, 1); 
        }
    }   

    TopSort();

    for(int i = 1; i <= n; i++) dist[i] = 1;
    for(int i = 0; i < n + m; i++){
        auto u = q[i];
        for(int k = h[u]; ~k; k = ne[k])
            dist[e[k]] = max(dist[e[k]], dist[u] + w[k]);
    }
    int res = 0;
    for(int i = 1; i <= n; i++) res = max(res, dist[i]);
    cout << res << endl;

    return 0;
}
/*
stop[1]: 1-6
10 --> 1 1
2 --> 10 0
10 --> 3 1
4 --> 10 0
10 --> 5 1
10 --> 6 1
stop[2]: 3-6
11 --> 3 1
4 --> 11 0
11 --> 5 1
11 --> 6 1
stop[3]: 1-9
12 --> 1 1
2 --> 12 0
3 --> 12 0
4 --> 12 0
12 --> 5 1
6 --> 12 0
7 --> 12 0
8 --> 12 0
12 --> 9 1
*/